666d1c
@@ -91,8 +91,9 @@
public abstract class AnnotationUtils {
 	 * Get a single {@link Annotation} of {@code annotationType} from the supplied
 	 * annotation: either the given annotation itself or a direct meta-annotation
 	 * thereof.
-	 * <p>Note that this method does <em>not</em> support arbitrary levels of
-	 * meta-annotations.
+	 * <p>Note that this method supports only a single level of meta-annotations.
+	 * For support for arbitrary levels of meta-annotations, use one of the
+	 * {@code find*()} methods instead.
 	 * @param ann the Annotation to check
 	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
 	 * @return the matching annotation, or {@code null} if not found
@@ -115,9 +116,11 @@
public abstract class AnnotationUtils {
 
 	/**
 	 * Get a single {@link Annotation} of {@code annotationType} from the supplied
-	 * {@link AnnotatedElement}.
-	 * <p>Meta-annotations will be searched if the annotation is not
-	 * <em>directly present</em> on the supplied element.
+	 * {@link AnnotatedElement}, where the {@code AnnotatedElement} is either
+	 * directly annotated or meta-annotated with the {@code annotationType}.
+	 * <p>Note that this method supports only a single level of meta-annotations.
+	 * For support for arbitrary levels of meta-annotations, use
+	 * {@link #findAnnotation(AnnotatedElement, Class)} instead.
 	 * @param annotatedElement the {@code AnnotatedElement} from which to get the annotation
 	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
 	 * @return the matching annotation, or {@code null} if not found
@@ -144,10 +147,13 @@
public abstract class AnnotationUtils {
 	}
 
 	/**
-	 * Get a single {@link Annotation} of {@code annotationType} from the supplied {@link Method}.
+	 * Get a single {@link Annotation} of {@code annotationType} from the
+	 * supplied {@link Method}, where the method is either directly annotated
+	 * or meta-annotated with the {@code annotationType}.
 	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
-	 * <p>Meta-annotations will be searched if the annotation is not
-	 * <em>directly present</em> on the supplied method.
+	 * <p>Note that this method supports only a single level of meta-annotations.
+	 * For support for arbitrary levels of meta-annotations, use
+	 * {@link #findAnnotation(Method, Class)} instead.
 	 * @param method the method to look for annotations on
 	 * @param annotationType the annotation type to look for
 	 * @return the matching annotation, or {@code null} if not found
@@ -256,26 +262,91 @@
public abstract class AnnotationUtils {
 	}
 
 	/**
-	 * Find a single {@link Annotation} of {@code annotationType} from the supplied
+	 * Find a single {@link Annotation} of {@code annotationType} on the
+	 * supplied {@link AnnotatedElement}.
+	 * <p>Meta-annotations will be searched if the annotation is not
+	 * <em>directly present</em> on the supplied element.
+	 * <p><strong>Warning</strong>: this method operates generically on
+	 * annotated elements. In other words, this method does not execute
+	 * specialized search algorithms for classes or methods. If you require
+	 * the more specific semantics of {@link #findAnnotation(Class, Class)}
+	 * or {@link #findAnnotation(Method, Class)}, invoke one of those methods
+	 * instead.
+	 * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation
+	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
+	 * @return the matching annotation, or {@code null} if not found
+	 * @since 4.2
+	 */
+	public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
+		// Do NOT store result in the findAnnotationCache since doing so could break
+		// findAnnotation(Class, Class) and findAnnotation(Method, Class).
+		return findAnnotation(annotatedElement, annotationType, new HashSet<Annotation>());
+	}
+
+	/**
+	 * Perform the search algorithm for {@link #findAnnotation(AnnotatedElement, Class)}
+	 * avoiding endless recursion by tracking which annotations have already
+	 * been <em>visited</em>.
+	 * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation
+	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
+	 * @param visited the set of annotations that have already been visited
+	 * @return the matching annotation, or {@code null} if not found
+	 * @since 4.2
+	 */
+	@SuppressWarnings("unchecked")
+	private static <T extends Annotation> T findAnnotation(AnnotatedElement annotatedElement, Class<T> annotationType, Set<Annotation> visited) {
+		Assert.notNull(annotatedElement, "AnnotatedElement must not be null");
+		try {
+			Annotation[] anns = annotatedElement.getDeclaredAnnotations();
+			for (Annotation ann : anns) {
+				if (ann.annotationType().equals(annotationType)) {
+					return (T) ann;
+				}
+			}
+			for (Annotation ann : anns) {
+				if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
+					T annotation = findAnnotation((AnnotatedElement) ann.annotationType(), annotationType, visited);
+					if (annotation != null) {
+						return annotation;
+					}
+				}
+			}
+		}
+		catch (Exception ex) {
+			// Assuming nested Class values not resolvable within annotation attributes...
+			logIntrospectionFailure(annotatedElement, ex);
+		}
+		return null;
+	}
+
+	/**
+	 * Find a single {@link Annotation} of {@code annotationType} on the supplied
 	 * {@link Method}, traversing its super methods (i.e., from superclasses and
 	 * interfaces) if no annotation can be found on the given method itself.
+	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
+	 * <p>Meta-annotations will be searched if the annotation is not
+	 * <em>directly present</em> on the method.
 	 * <p>Annotations on methods are not inherited by default, so we need to handle
 	 * this explicitly.
-	 * <p>Meta-annotations will <em>not</em> be searched.
 	 * @param method the method to look for annotations on
 	 * @param annotationType the annotation type to look for
 	 * @return the matching annotation, or {@code null} if not found
+	 * @see #getAnnotation(Method, Class)
 	 */
 	@SuppressWarnings("unchecked")
 	public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType) {
 		AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType);
 		A result = (A) findAnnotationCache.get(cacheKey);
+
 		if (result == null) {
-			result = getAnnotation(method, annotationType);
-			Class<?> clazz = method.getDeclaringClass();
+			Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
+			result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType);
+
 			if (result == null) {
-				result = searchOnInterfaces(method, annotationType, clazz.getInterfaces());
+				result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces());
 			}
+
+			Class<?> clazz = method.getDeclaringClass();
 			while (result == null) {
 				clazz = clazz.getSuperclass();
 				if (clazz == null || clazz.equals(Object.class)) {
@@ -283,7 +354,8 @@
public abstract class AnnotationUtils {
 				}
 				try {
 					Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
-					result = getAnnotation(equivalentMethod, annotationType);
+					Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod);
+					result = findAnnotation((AnnotatedElement) resolvedEquivalentMethod, annotationType);
 				}
 				catch (NoSuchMethodException ex) {
 					// No equivalent method found
@@ -292,9 +364,10 @@
public abstract class AnnotationUtils {
 					result = searchOnInterfaces(method, annotationType, clazz.getInterfaces());
 				}
 			}
-			if (result != null) {
-				findAnnotationCache.put(cacheKey, result);
-			}
+		}
+
+		if (result != null) {
+			findAnnotationCache.put(cacheKey, result);
 		}
 		return result;
 	}
@@ -680,7 +753,7 @@
public abstract class AnnotationUtils {
 	}
 
 	/**
-	 * Retrieve the <em>value</em> of the {@code &quot;value&quot;} attribute of a
+	 * Retrieve the <em>value</em> of the {@code value} attribute of a
 	 * single-element Annotation, given an annotation instance.
 	 * @param annotation the annotation instance from which to retrieve the value
 	 * @return the attribute value, or {@code null} if not found
@@ -712,7 +785,7 @@
public abstract class AnnotationUtils {
 	}
 
 	/**
-	 * Retrieve the <em>default value</em> of the {@code &quot;value&quot;} attribute
+	 * Retrieve the <em>default value</em> of the {@code value} attribute
 	 * of a single-element Annotation, given an annotation instance.
 	 * @param annotation the annotation instance from which to retrieve the default value
 	 * @return the default value, or {@code null} if not found
@@ -737,7 +810,7 @@
public abstract class AnnotationUtils {
 	}
 
 	/**
-	 * Retrieve the <em>default value</em> of the {@code &quot;value&quot;} attribute
+	 * Retrieve the <em>default value</em> of the {@code value} attribute
 	 * of a single-element Annotation, given the {@link Class annotation type}.
 	 * @param annotationType the <em>annotation type</em> for which the default value should be retrieved
 	 * @return the default value, or {@code null} if not found
